package designPatterns;

import java.util.HashMap;
import java.util.Map;

/**
* @author jie
* @version 创建时间：2017年10月30日 下午10:34:54
* 懒汉式和饿汉式,一个是拿时间换空间,一个是拿空间换时间,
* 	懒汉式只有我需要他的时候才去加载它,懒加载机制,
* 	饿汉式不管需不需要我先加载了再说,先在内存中开辟一块空间,占用一块地方,等用到了直接就拿来用.
* 	这两种是最基本的单列模式。
* 	懒汉式缺点:效率低，第一次加载需要实例化，反应稍慢。每次调用 getInstance 方法都会进行同步，消耗不必要的资源。
*	饿汉式缺点：不需要的时候就加载了,造成资源浪费。
*
*双重检查单例( DCL 实现单例)：
 *
 *静态内部类
 * * TODO 重点 外部类加载时并不需要立即加载内部类，内部类不被加载则不去初始化INSTANCE，故而不占内存。
 *  * 即当SingleTon第一次被加载时，并不需要去加载SingleTonHoler，只有当getInstance()方法第一次被调用时，才会去初始化INSTANCE,
 *  * 第一次调用getInstance()方法会导致虚拟机加载SingleTonHoler类，这种方法不仅能确保线程安全，也能保证单例的唯一性，同时也延迟了单例的实例化。
*
*/
public class SingletonPattern {

	public static void main(String[] args) {
		Singleton_D.methodA();
	}
}

// 饿汉式
class Singleton_A {
	private static final Singleton_A INSTANCE = new Singleton_A();

	private Singleton_A() {
		super();
	}

	public static Singleton_A getInstance() {
		return INSTANCE;
	}
}

// 懒汉式
class Singleton_B {
	private static Singleton_B INSTANCE = new Singleton_B();

	private Singleton_B() {
		super();
	}

	public static synchronized Singleton_B getInstance() {
		if (INSTANCE == null) {
			INSTANCE = new Singleton_B();
		}
		return INSTANCE;
	}
}

// 双重检查单例( DCL 实现单例)：
/**
 *
 ** @author jie
 * 简单总结就是说jdk1.5之前会造成两个问题:
 * https://www.zhihu.com/question/46903811 重排序 问题

线程间共享变量不可见性;

无序性(执行顺序无法保证);

当然这个bug已经修复了,SUN官方调整了JVM,具体了Volatile关键字(eg : EventBus),因此在jdk1.5之前只需要写成这样既可,
  private Volatitle static Singleton instance;
  这样就可以保证每次都是从主内存中取,当然这样写或多或少的回影响性能,但是为了安全起见,这点性能牺牲还是值得。

双重检查单列(DCL)

优点:资源利用率高,第一次执行方法是单例对象才会被实例化;

缺点:第一次加载时会稍慢,jdk1.5之之前有可能会加载会失败;

 *
 *思考 1
 *如果 没有volatile
 *这段代码看起来很完美，很可惜，它是有问题。主要在于instance = new Singleton()这句，
 *这并非是一个原子操作，事实上在 JVM 中这句话大概做了下面 3 件事情。

1 给 instance 分配内存
2 调用 Singleton 的构造函数来初始化成员变量
3 将instance对象指向分配的内存空间（执行完这步 instance 就为非 null 了）
但是在 JVM 的即时编译器中存在指令重排序的优化。也就是说上面的第二步和第三步的顺序是不能保证的，
最终的执行顺序可能是 1-2-3 也可能是 1-3-2。如果是后者，
则在 3 执行完毕、2 未执行之前，被线程二抢占了，这时 instance 已经是非 null 了（但却没有初始化），
所以线程二会直接返回 instance，然后使用，然后顺理成章地报错。

*
*思考 2
*在上面这行代码中，没有使用volatile关键字，的确即使发生了指令重排序，
*在同步代码块结束时，instance会被正确的初始化。
*
*但是，假如你在其他线程中使用了这个static instance的静态变量，用instance==null的方法判断instance有没有被正确初始化的话，
*有可能会出现instance访问失败的情况。
*在这个时间点上，同步代码块尚未执行完毕，由于指令重排序，instance对象已经指向分配的内存空间，
*但是instance尚未初始化完毕。在这时调用instance，会引发jvm的exception
 */
class Singleton_C {
	private volatile static Singleton_C INSTANCE = new Singleton_C();

	private Singleton_C() {
		super();
	}

	public static Singleton_C getInstance() {
		if (INSTANCE == null) {
			synchronized (Singleton_C.class) {
				if (INSTANCE == null) {
					INSTANCE = new Singleton_C();
				}
			}
		}
		return INSTANCE;
	}
}

/**
 * 静态内部内实现单例：
 * 这种方式不仅确保了线程的安全性,也能够保证对象的唯一性,同时也是延迟加载,很多技术大牛也是这样推荐书写。
 *
 * 因为内部静态类是要在有引用了以后才会装载到内存的。
 * @author jie
 *
 * TODO 重点 外部类加载时并不需要立即加载内部类，内部类不被加载则不去初始化INSTANCE，故而不占内存。
 * 即当SingleTon第一次被加载时，并不需要去加载SingleTonHoler，只有当getInstance()方法第一次被调用时，才会去初始化INSTANCE,
 * 第一次调用getInstance()方法会导致虚拟机加载SingleTonHoler类，这种方法不仅能确保线程安全，也能保证单例的唯一性，同时也延迟了单例的实例化。
 *
 */
class Singleton_D {

	private static Singleton_D instance;

	public static void methodA(){
      System.out.println("Singleton static methodA");
  }

	static {
		System.out.println("Singleton_D static block");
	}

	{
		System.out.println("Singleton_D block");
	}
	private Singleton_D() {
		System.out.println("Singleton_D constructor");
	}

	public static class SingletonInstance {
		public int abc = getnum();

		private int getnum() {
			System.err.println(" inner static class  method filed ");
			return 0;
		}

		{
			System.err.println(" inner static class  block");
		}
		static {
			System.err.println(" inner static class  static block");
		}
		private static final Singleton_D INSTANCE = new Singleton_D();
	}

	public static Singleton_D getInstance() {
		return SingletonInstance.INSTANCE;
	}
}

class Singleton_E {

	private static Singleton_E instance;

	public static void methodA(){
      System.out.println("Singleton static methodA");
  }

	static {
		System.out.println("Singleton_D static block");
	}

	{
		System.out.println("Singleton_D block");
	}
	private Singleton_E() {
		System.out.println("Singleton_D constructor");
	}

	public static class SingletonInstance {
		public int abc = getnum();

		private int getnum() {
			System.err.println(" inner static class  method filed ");
			return 0;
		}

		{
			System.err.println(" inner static class  block");
		}
		static {
			System.err.println(" inner static class  static block");
		}
		private static final Singleton_E INSTANCE = new Singleton_E();
	}

	private static int num ;
	public static Singleton_E getInstance(int num) {
		Singleton_E.num = num;
		return SingletonInstance.INSTANCE;
	}
}

/**
 * 枚举实现单例：
 * 优点:相对于其他单例来说枚举写法最简单,并且任何情况下都是单列的,JDK1.5之后才有的。
 * @author jie
 *
 */
enum SingletonEnum {

	INSTANCE;
	public void doSomething() {

	}
}

/**
 * 使用容器单例：
 *
 * 在程序开始的时候将单例类型注入到一个容器之中,也就是单例 ManagerClass ,
 * 在使用的时候再根据 key 值获取对应的实例,
 * 这种方式可以使我们很方便的管理很多单例对象,也对用户隐藏了具体实现类,降低了耦合度;
 * 但是为了避免造成内存泄漏,所以我们一般在生命周期销毁的时候也要去销毁它。
 * @author jie
 *
 */
class SingletonManager {

	private static Map<String, Object> objMap = new HashMap();

	private SingletonManager() {

	}

	public static void putObject(String key, String instance) {
		if (!objMap.containsKey(key)) {
			objMap.put(key, instance);
		}
	}

	public static Object getObject(String key) {
		return objMap.get(key);
	}
}
